package ac

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"time"

	"github.com/garyburd/redigo/redis"
)

type Controller struct {
	config *Config
	pool   *redis.Pool
}

type Config struct {
	RedisUrl string
	Expire   int
	Key      string
}

const (
	DefaultRedisUrl string = ":6379"
	DefaultExpire   int    = 24 * 60 * 60
	DefaultKey      string = "ac2.0_20220620"
	Prefix          string = "ac:"
)

func New(cfg *Config) (*Controller, error) {
	if cfg.RedisUrl == "" {
		cfg.RedisUrl = DefaultRedisUrl
	}
	if cfg.Expire == 0 {
		cfg.Expire = DefaultExpire
	}
	if cfg.Key == "" {
		cfg.Key = DefaultKey
	}
	ac := Controller{config: cfg}

	ac.pool = &redis.Pool{
		MaxIdle:     10,
		MaxActive:   0,
		IdleTimeout: 300,
		Dial: func() (redis.Conn, error) {
			return redis.Dial("tcp", cfg.RedisUrl)
		},
	}

	return &ac, nil
}

func (p *Controller) CreateToken() string {
	i := time.Now().UnixNano()
	s := fmt.Sprintf("%d%s", i, p.config.Key)

	md5Ctx := md5.New()
	md5Ctx.Write([]byte(s))
	cipherStr := md5Ctx.Sum(nil)
	token := hex.EncodeToString(cipherStr)

	return token
}

func (p *Controller) SaveToken(userId string) string {
	cli := p.pool.Get()

	defer cli.Close()

	token := p.CreateToken()
	key := Prefix + userId

	cli.Do("SET", key, token, "EX", p.config.Expire)

	return token
}

func (p *Controller) GetToken(userId string) string {
	cli := p.pool.Get()

	defer cli.Close()

	key := Prefix + userId
	token, err := redis.String(cli.Do("GET", key))
	if err != nil {
		return ""
	}
	return token
}

func (p *Controller) Check(userId string, token string) bool {
	if userId == "" || token == "" {
		return false
	}

	tk := p.GetToken(userId)
	return token == tk
}

func (p *Controller) ResetExpire(userid string) {
	cli := p.pool.Get()
	defer cli.Close()

	key := Prefix + userid
	cli.Do("EXPIRE", key, p.config.Expire)
}

func (p *Controller) Close() {
	p.pool.Close()
}
